package com.mouchel.gwt.xpath.client;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.user.client.Window;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.Attr;
import com.google.gwt.xml.client.Node;
import com.mouchel.gwt.xpath.client.impl.XPathImpl;

public class XPath
{
	private final static XPathImpl impl = XPathImpl.getInstance();
	
	private static boolean checkType(Node node, Class<? extends Node> type)
	{
		switch (node.getNodeType())
		{
			case Node.ELEMENT_NODE:
				return type == com.google.gwt.xml.client.Element.class;
			case Node.ATTRIBUTE_NODE:
				return type == com.google.gwt.xml.client.Attr.class;
			case Node.TEXT_NODE:
				return type == com.google.gwt.xml.client.Text.class;
			case Node.CDATA_SECTION_NODE:
				return type == com.google.gwt.xml.client.CDATASection.class;
			case Node.ENTITY_REFERENCE_NODE:
				return type == com.google.gwt.xml.client.EntityReference.class;
			case Node.ENTITY_NODE:
				return false;
			case Node.PROCESSING_INSTRUCTION_NODE:
				return type == com.google.gwt.xml.client.ProcessingInstruction.class;
			case Node.COMMENT_NODE:
				return type == com.google.gwt.xml.client.Comment.class;
			case Node.DOCUMENT_NODE:
				return type == com.google.gwt.xml.client.Document.class;
			case Node.DOCUMENT_TYPE_NODE:
				return false;
			case Node.DOCUMENT_FRAGMENT_NODE:
				return type == com.google.gwt.xml.client.DocumentFragment.class;
			case Node.NOTATION_NODE:
				return false;
			default:
				return false;
		}
	}
	
	public static List<Node> evaluate(Element element, String xpath)
	{
		return impl.evaluate(element, xpath);
	}	
	
	public static List<Node> evaluate(Document document, String xpath)
	{
		return evaluate(document.getDocumentElement(), xpath);
	}	 
	
	public static<T extends Node> List<T> evaluate(Element element, String xpath, Class<T> type)
	{
		List<Node> results = evaluate(element, xpath);
		List<T> fr = new ArrayList<T>();
		
		for (int n = 0; n < results.size(); n++)
		{
			Node result = results.get(n);
			if (checkType(result, type))
			{
				fr.add((T) results.get(n));
			}
		}
		
		return fr;
	}
	
	public static<T extends Node> List<T> evaluate(Document document, String xpath, Class<T> type)
	{
		return evaluate(document.getDocumentElement(), xpath, type);
	}
	
	public static Node evaluateSingle(Element element, String xpath)
	{
		List<Node> nodes = evaluate(element, xpath);
		if (nodes.size() == 1)
		{
			return nodes.get(0);
		}
		else return null;
	}
	
	public static Node evaluateSingle(Document document, String xpath)
	{
		return evaluateSingle(document.getDocumentElement(), xpath);
	}
	
	public static<T extends Node> T evaluateSingle(Element element, String xpath, Class<T> type)
	{
		List<T> nodes = evaluate(element, xpath, type);
		if (nodes.size() == 1)
		{
			return nodes.get(0);
		}
		else return null;
	}
	
	public static<T extends Node> T evaluateSingle(Document document, String xpath, Class<T> type)
	{
		return evaluateSingle(document.getDocumentElement(), xpath, type);
	}
}